"""FastAPI routes for VectorDB microservice."""

import asyncio
import logging
import json
from typing import List, Dict, Any, Optional
from datetime import datetime

from fastapi import APIRouter, HTTPException, Query, BackgroundTasks
from fastapi.responses import JSONResponse

from api.models import (
    EmbeddingRequest, EmbeddingResponse, VectorStoreRequest, VectorSearchRequest,
    VectorSearchResponse, VectorDeleteRequest, TableCreateRequest, TableInfoResponse,
    DatabaseStatsResponse, HealthResponse, ProviderInfoResponse, ErrorResponse,
    SuccessResponse, RagQueryRequest, RagQueryResponse, WebSocketMessage
)
from core.database import get_db_manager
from core.embedding import get_embedding_service, embed_texts, embed_text
from core.config import get_config

logger = logging.getLogger(__name__)

# Create API router
router = APIRouter()


@router.get("/health", response_model=HealthResponse)
async def health_check():
    """Health check endpoint."""
    try:
        # Check database
        db_manager = await get_db_manager()
        db_stats = await db_manager.get_database_stats()
        db_healthy = "error" not in db_stats

        # Check embedding service
        embedding_service = await get_embedding_service()
        provider_health = await embedding_service.health_check()
        embedding_healthy = any(provider_health.values())

        return HealthResponse(
            status="healthy" if db_healthy and embedding_healthy else "degraded",
            timestamp=datetime.now(),
            database={
                "healthy": db_healthy,
                "stats": db_stats if db_healthy else {}
            },
            embedding={
                "healthy": embedding_healthy,
                "providers": provider_health
            }
        )

    except Exception as e:
        logger.error(f"Health check failed: {e}")
        raise HTTPException(status_code=503, detail=f"Service unhealthy: {e}")


@router.get("/providers", response_model=ProviderInfoResponse)
async def get_providers():
    """Get available embedding providers."""
    try:
        embedding_service = await get_embedding_service()
        available_providers = embedding_service.get_available_providers()
        current_provider = embedding_service.get_current_provider()

        config = get_config()
        provider_configs = {}
        for provider in available_providers:
            if provider in config.embedding.providers:
                provider_configs[provider] = {
                    "base_url": config.embedding.providers[provider].base_url,
                    "models": config.embedding.providers[provider].models
                }

        return ProviderInfoResponse(
            available_providers=available_providers,
            current_provider=current_provider,
            provider_configs=provider_configs
        )

    except Exception as e:
        logger.error(f"Failed to get providers: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to get providers: {e}")


@router.post("/providers/{provider_name}/switch", response_model=SuccessResponse)
async def switch_provider(provider_name: str):
    """Switch embedding provider."""
    try:
        embedding_service = await get_embedding_service()
        success = await embedding_service.set_provider(provider_name)

        if success:
            return SuccessResponse(
                message=f"Successfully switched to provider: {provider_name}"
            )
        else:
            raise HTTPException(
                status_code=400,
                detail=f"Provider {provider_name} not available or failed to switch"
            )

    except Exception as e:
        logger.error(f"Failed to switch provider: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to switch provider: {e}")


@router.post("/embed", response_model=EmbeddingResponse)
async def embed_texts_endpoint(request: EmbeddingRequest):
    """Generate embeddings for texts."""
    try:
        embeddings = await embed_texts(request.texts, request.provider)

        embedding_service = await get_embedding_service()
        current_provider = embedding_service.get_current_provider()
        config = get_config()

        return EmbeddingResponse(
            embeddings=embeddings,
            provider=current_provider or "unknown",
            model=config.embedding.model,
            dimensions=len(embeddings[0]) if embeddings else 0
        )

    except Exception as e:
        logger.error(f"Embedding generation failed: {e}")
        raise HTTPException(status_code=500, detail=f"Embedding generation failed: {e}")


@router.post("/tables", response_model=SuccessResponse)
async def create_table(request: TableCreateRequest):
    """Create a new vector table."""
    try:
        db_manager = await get_db_manager()
        await db_manager.create_table(
            table_name=request.table_name,
            vector_dim=request.vector_dimension,
            overwrite=request.overwrite
        )

        return SuccessResponse(
            message=f"Table '{request.table_name}' created successfully"
        )

    except Exception as e:
        logger.error(f"Failed to create table: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to create table: {e}")


@router.get("/tables", response_model=List[str])
async def list_tables():
    """List all tables."""
    try:
        db_manager = await get_db_manager()
        tables = await db_manager.list_tables()
        return tables

    except Exception as e:
        logger.error(f"Failed to list tables: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to list tables: {e}")


@router.get("/tables/{table_name}", response_model=TableInfoResponse)
async def get_table_info(table_name: str):
    """Get table information."""
    try:
        db_manager = await get_db_manager()
        info = await db_manager.get_table_info(table_name)

        if not info:
            raise HTTPException(status_code=404, detail=f"Table '{table_name}' not found")

        return TableInfoResponse(
            name=info["name"],
            schema=info["schema"],
            row_count=info["row_count"],
            created_at=info.get("info", {}).get("created_at")
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Failed to get table info: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to get table info: {e}")


@router.delete("/tables/{table_name}")
async def delete_table(table_name: str):
    """Delete a table."""
    try:
        # LanceDB doesn't have a direct delete table API, so we'll create an empty table with overwrite
        db_manager = await get_db_manager()
        await db_manager.create_table(table_name, overwrite=True)

        return SuccessResponse(
            message=f"Table '{table_name}' deleted successfully"
        )

    except Exception as e:
        logger.error(f"Failed to delete table: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to delete table: {e}")


@router.post("/store", response_model=SuccessResponse)
async def store_vectors(request: VectorStoreRequest):
    """Store vectors in database."""
    try:
        db_manager = await get_db_manager()

        # Generate embeddings if vectors not provided
        if request.vectors is None:
            embeddings = await embed_texts(request.texts, request.provider)
        else:
            embeddings = request.vectors

        # Prepare data for insertion
        data = []
        for i, (text, embedding) in enumerate(zip(request.texts, embeddings)):
            item = {
                "vector": embedding,
                "text": text,
                "metadata": json.dumps(request.metadata[i]) if request.metadata and i < len(request.metadata) else "{}",
                "created_at": datetime.now()
            }
            data.append(item)

        # Insert data
        success = await db_manager.insert_data(request.table_name, data)

        if success:
            return SuccessResponse(
                message=f"Successfully stored {len(data)} vectors in table '{request.table_name}'"
            )
        else:
            raise HTTPException(status_code=500, detail="Failed to store vectors")

    except Exception as e:
        logger.error(f"Failed to store vectors: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to store vectors: {e}")


@router.post("/search", response_model=VectorSearchResponse)
async def search_vectors(request: VectorSearchRequest):
    """Search vectors in database."""
    try:
        db_manager = await get_db_manager()

        # Generate embedding for query
        query_embedding = await embed_text(request.query, request.provider)

        # Perform search
        results = await db_manager.search(
            table_name=request.table_name,
            query_vector=query_embedding,
            limit=request.limit,
            threshold=request.threshold
        )

        # Process results
        processed_results = []
        for result in results:
            processed_result = {
                "text": result.get("text", ""),
                "metadata": json.loads(result.get("metadata", "{}")),
                "score": result.get("_distance", 0.0),
                "created_at": result.get("created_at", "").isoformat() if result.get("created_at") else None
            }
            processed_results.append(processed_result)

        return VectorSearchResponse(
            results=processed_results,
            count=len(processed_results),
            query=request.query,
            table_name=request.table_name
        )

    except Exception as e:
        logger.error(f"Vector search failed: {e}")
        raise HTTPException(status_code=500, detail=f"Vector search failed: {e}")


@router.post("/delete", response_model=SuccessResponse)
async def delete_vectors(request: VectorDeleteRequest):
    """Delete vectors from database."""
    try:
        db_manager = await get_db_manager()
        success = await db_manager.delete_data(request.table_name, request.conditions)

        if success:
            return SuccessResponse(
                message=f"Successfully deleted vectors from table '{request.table_name}'"
            )
        else:
            raise HTTPException(status_code=500, detail="Failed to delete vectors")

    except Exception as e:
        logger.error(f"Failed to delete vectors: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to delete vectors: {e}")


@router.get("/stats", response_model=DatabaseStatsResponse)
async def get_database_stats():
    """Get database statistics."""
    try:
        db_manager = await get_db_manager()
        stats = await db_manager.get_database_stats()

        if "error" in stats:
            raise HTTPException(status_code=500, detail=stats["error"])

        return DatabaseStatsResponse(**stats)

    except Exception as e:
        logger.error(f"Failed to get database stats: {e}")
        raise HTTPException(status_code=500, detail=f"Failed to get database stats: {e}")


@router.post("/rag/search", response_model=RagQueryResponse)
async def rag_search(request: RagQueryRequest):
    """RAG (Retrieval-Augmented Generation) search endpoint."""
    try:
        db_manager = await get_db_manager()

        # Generate embedding for query
        query_embedding = await embed_text(request.query, request.provider)

        # Perform search
        results = await db_manager.search(
            table_name=request.table_name,
            query_vector=query_embedding,
            limit=request.limit,
            threshold=request.threshold
        )

        # Format results for RAG
        context = []
        for result in results:
            context_item = {
                "text": result.get("text", ""),
                "metadata": json.loads(result.get("metadata", "{}")),
                "score": result.get("_distance", 0.0),
                "created_at": result.get("created_at", "").isoformat() if result.get("created_at") else None
            }
            context.append(context_item)

        return RagQueryResponse(
            query=request.query,
            context=context,
            context_count=len(context),
            table_name=request.table_name
        )

    except Exception as e:
        logger.error(f"RAG search failed: {e}")
        raise HTTPException(status_code=500, detail=f"RAG search failed: {e}")


@router.post("/backup")
async def create_backup(background_tasks: BackgroundTasks):
    """Create database backup."""
    try:
        db_manager = await get_db_manager()

        # Save state in background
        background_tasks.add_task(db_manager.save_state)

        return SuccessResponse(
            message="Backup creation started in background"
        )

    except Exception as e:
        logger.error(f"Backup creation failed: {e}")
        raise HTTPException(status_code=500, detail=f"Backup creation failed: {e}")


# Note: Error handlers are defined in main.py on the FastAPI app instance